import React from 'react';
import classNames from 'classnames';
import FormItem from '../FormItem';
import Icon from '../Icon';
import Input from '../Input';
import Dom from '../utils/Dom';
import Datum from './Datum';
import Text from './Text';
import {alignPostion} from '../utils/Utils';
import { Scrollbars } from 'react-custom-scrollbars';

import './Select.less';
import CheckBox from '../Input/Checkbox';

// eslint-disable-next-line react/display-name
class Option extends React.Component {
    constructor (props) {
        super(props);

        props.datum.bind(props.id, this.forceUpdate.bind(this));
    }

    componentWillUnmount () {
        this.props.datum.unbind(this.props.id);
    }

    onClick () {
        const { datum, id, multi, item } = this.props;
        if (item.disabled) {
            return;
        }
        const checked = datum.getChecked(id);
        if (multi) {
            datum.set(id, checked ? 0 : 1);
        } else {
            datum.set(id, 1);
        }
        if (this.props.onChange) {
            this.props.onChange(datum.getValue(), item);
        }
    }

    render () {
        const {format, item, textField, datum, id, enableCheckbox} = this.props;
        const checked = datum.getChecked(id);
        const clazzName = classNames('cm-select-option', {
            'cm-select-group-wrap': item.children,
            'cm-select-option-active': checked,
            'cm-select-option-disabled': item.disabled,
            'cm-select-option-hidden': item._hidden
        });
        let display = item[textField];
        if (format && typeof format === 'function') {
            display = format (item);
        }
        if (item.children) {
            display = <div className='cm-select-group-title'>{display}</div>;
        }
        return <li className={clazzName} onClick={item.children ? undefined : this.onClick.bind(this)}>
            {
                enableCheckbox && !item.children ? <CheckBox checked={checked} disabled={item.disabled}/> : null
            }
            {display}
        </li>;
    }
}

class Select extends React.Component {
    static displayName = 'Select';

    static defaultProps = {
        textField: 'text',
        valueField: 'id',
        sep: ',',
        active: false,
        value: '',
        group: false,
        align: 'bottomLeft',
        clearable: false,
        height: 250,
        multi: false,
        enableCheckbox: false,
        pageOffset: 3
    };

    pageSize = 10;

    state = {
        dropup: false,
        open: false,
        pageNum: 0,
        optionStyle: {}
    }

    constructor (props) {
        super(props);

        const v = props.value ? (typeof props.value === 'string' ? props.value.split(',') : props.value || []) : [];
        this.datum = new Datum({
            value: v,
            data: props.data,
            valueField: props.valueField,
            textField: props.textField,
            multi: props.multi
        });
    }

    renderText () {
        return <Text datum={this.datum}/>;
    }

    renderFilter () {
        if (this.props.filter) {
            return <div className='cm-select-filter-wrap'>
                <Input className='cm-select-filter' onInput={this.onFilter} size='small'/>
            </div>;
        } else {
            return null;
        }
    }

    onFilter = (e) => {
        const value = e.target.value;
        this.datum.filter(value);

        this.resizeHeight();
        this.forceUpdate();
    }

    renderPageOptions () {
        const {value, data, textField, valueField, template, pageOffset, multi, enableCheckbox} = this.props;
        if (!data) {
            return null;
        }
        if (!this.scroll) {
            return;
        }
        const showData = this.datum.data;
        const currentPage = this.state.pageNum;
        const start = Math.max((currentPage - 1) * this.pageSize, 0);
        const end = Math.min(start + pageOffset * this.pageSize, showData.length);

        const renderData = [];
        for (let i = start; i < end; i++) {
            renderData.push(showData[i]);
        }
        
        if (renderData.length) {
            return renderData.map(item => {
                return <Option id={`${item[valueField]}`} format={template} item={item} 
                    key={item[valueField]} datum={this.datum} multi={multi} enableCheckbox={enableCheckbox}
                    textField={textField} valueField={valueField} onChange={this.onChange.bind(this)} />;
            });
        } else {
            return <li className='cm-select-option cm-select-no-data'>无匹配数据</li>;
        }
    }

    onChange (v, item) {
        if (!this.props.multi && this._isMounted) {
            this.setState({
                open: false
            });
        }
        this.props.onChange && this.props.onChange(v.join(','), item);
    }

    /**
     * 清除
     */
    onClear = () => {
        if (this.props.onClear) {
            this.props.onClear();
        }
        if (this.props.onChange) {
            this.props.onChange('');
            this.datum.clear();
        }
    }

    onShowHide = (e) => {
        const {disabled, align} = this.props;
        const ele = e.target || e.srcElement;
        // click clear icon
        if (ele.tagName === 'I' && ele.className.indexOf('cm-select-clear') !== -1) {
            return;
        }
        if (disabled) {
            return;
        }
        const open = this.state.open;
        this.setState({
            open: !open
        }, () => {
            const optionStyle = alignPostion(align, this.rel, this.optionsWrap);
            this.setState({
                optionStyle
            });
        });
    }

    componentDidMount () {
        this._isMounted = true;
        document.addEventListener('click', this.clickAnyWhere.bind(this), false);
        this.resizeHeight();
    }

    componentWillUnmount () {
        this._isMounted = false;
        document.removeEventListener('click', this.clickAnyWhere);
    }

    clickAnyWhere (e) {
        const ele = e.target || e.srcElement;
        const parent = Dom.closest(ele, '.cm-select');
        if (parent && parent == this.wrap) {
            return ;
        } else {
            this._isMounted && this.setState({
                open: false
            });
        }
    }

    resizeHeight () {
        if (!this.itemHeight) {
            this.optionsWrap.style.display = 'block';
            const li = document.createElement('li');
            li.className = 'cm-select-option';
            li.innerHTML = 'aaa';
            this.list.appendChild(li);
            const optionRect = li.getBoundingClientRect();
            this.itemHeight = optionRect.height;
            this.list.removeChild(li);
            this.optionsWrap.style.display = '';
        }
        const {data} = this.props;
        if (data) {
            const allHeight = Math.max(this.datum.getVisibleDataLength(), 1) * this.itemHeight;
            this.list.style.height = `${allHeight}px`;
        }
        if (this.scroll) {
            this.scroll.scrollTop(0);
        }
    }

    onScrollFrame = (e) => {
        const sizes = e.scrollTop / (this.itemHeight || 30);
        const currentPage = parseInt(sizes / this.pageSize, 10);
        if (currentPage !== this.state.pageNum) {
            this.setState({
                pageNum: currentPage
            });
        }
    }

    componentDidUpdate (prevProps) {
        if (prevProps.data != this.props.data) {
            this.datum.setData(this.props.data);
            this.resizeHeight();
        }
        if (this.props.onChange) {
            if (prevProps.value !== this.props.value) {
                const v = this.props.value ? this.props.value.split(',') : [];
                this.datum.setValue(v);
            }
        }
    }

    render () {
        let {className, style, multi, disabled, size, clearable, name, value, prefix, height} = this.props;
        className = classNames('cm-select', className, {
            'cm-select-disabled': disabled,
            'cm-select-dropup': this.state.dropup,
            [`cm-select-${size}`]: size,
            'cm-select-clearable': !disabled && clearable && value !== '',
            'cm-select-multi': multi,
            'cm-select-open': this.state.open,
            'cm-select-with-prefix': prefix,
            'cm-select-hasEmptyOption': !multi && this.props.hasEmptyOption
        });

        const start = Math.max(this.state.pageNum - 1, 0);
        const paddingTop = start * this.itemHeight * this.pageSize || 0;

        return <div className={className} style={style} ref={f => this.wrap = f}>
            <div className='cm-select-value' tabIndex='0' onClick={this.onShowHide} ref={f => this.rel = f}>
                <input type='hidden' name={name} value={value}/>
                {
                    prefix
                        ? <span className='cm-select-prefix'>{prefix}</span>
                        : null
                }
                {this.renderText()}
                <Icon name='chevron-down' className='cm-select-cert'/>
                {
                    clearable
                        ? <Icon name='x-circle' className='cm-select-clear' onClick={this.onClear}/>
                        : null
                }
            </div>
            <div className='cm-select-options-wrap' ref={f => this.optionsWrap = f} style={this.state.optionStyle}>
                <div className='cm-select-options'>
                    {this.renderFilter()}
                    <Scrollbars autoHeight autoHeightMax={height} ref={f => this.scroll = f} onScrollFrame={this.onScrollFrame}>
                        <ul className='cm-select-option-list' ref={f => this.list = f} style={{paddingTop}}>
                            {this.renderPageOptions()}
                        </ul>
                    </Scrollbars>
                </div>
            </div>
        </div>;
    }
}

FormItem.register(Select, 'select');

export default Select;
